using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Reflection;
using System.ComponentModel;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Opc.Ua;
using Opc.Ua.Client;
// using Opc.Ua.Configuration; // Likely handled by OpcUaHelper or different approach
using OpcUaHelper;
using twistmachinev2.CommonUse; // Added OpcUaHelper

namespace twistmachinev2
{
    /// <summary>
    /// 操作结果类
    /// </summary>
    /// <typeparam name="T">结果数据类型</typeparam>
    public class OperateResult<T>
    {
        /// <summary>
        /// 是否成功
        /// </summary>
        public bool IsSuccess { get; set; }

        /// <summary>
        /// 错误消息
        /// </summary>
        public string Message { get; set; }

        /// <summary>
        /// 结果数据
        /// </summary>
        public T Content { get; set; }

        public OperateResult()
        {
            IsSuccess = true;
        }

        public OperateResult(string message)
        {
            IsSuccess = false;
            Message = message;
        }

        public OperateResult(T content)
        {
            IsSuccess = true;
            Content = content;
        }
    }

    /// <summary>
    /// 操作结果类
    /// </summary>
    public class OperateResult
    {
        /// <summary>
        /// 是否成功
        /// </summary>
        public bool IsSuccess { get; set; }

        /// <summary>
        /// 错误消息
        /// </summary>
        public string Message { get; set; }

        public OperateResult()
        {
            IsSuccess = true;
        }

        public OperateResult(string message)
        {
            IsSuccess = false;
            Message = message;
        }
    }

    /// <summary>
    /// OPCUA客户端管理类
    /// </summary>
    public class OpcUaClient : IDisposable
    {
        private OpcUaHelper.OpcUaClient m_OpcUaClient; // OpcUaHelper instance
        private bool isConnected = false;
        // Subscriptions and monitored items will likely be managed differently with OpcUaHelper
        // For example, OpcUaHelper might handle subscriptions internally or provide a different API
        // We will adapt this as we implement the subscription methods

        /// <summary>
        /// 连接状态改变事件
        /// </summary>
        public event Action<bool> ConnectionStatusChanged;

        /// <summary>
        /// 数据变化事件
        /// </summary>
        public event Action<string, object> DataChanged;

        /// <summary>
        /// 构造函数
        /// </summary>
        public OpcUaClient()
        {
            // Instantiate OpcUaHelper's client
            m_OpcUaClient = new OpcUaHelper.OpcUaClient();

            // Configuration and certificate management are likely handled by OpcUaHelper.
            // If specific configuration is needed, it would be done here or before connecting.
            // For now, we assume OpcUaHelper handles defaults or provides methods to set them.
        }

        // The InitializeConfiguration and CheckApplicationCertificate methods are removed
        // as their functionality is expected to be covered by OpcUaHelper.

        /// <summary>
        /// 连接到OPCUA服务器
        /// </summary>
        /// <param name="serverUrl">服务器地址</param>
        /// <param name="username">用户名</param>
        /// <param name="password">密码</param>
        /// <returns>连接结果</returns>
        public async Task<OperateResult> ConnectAsync(string serverUrl, string username = "", string password = "")
        {
            try
            {
                // 如果已连接，先断开
                if (isConnected)
                {
                    Disconnect();
                }

                // 设置用户身份
                if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
                {
                    m_OpcUaClient.UserIdentity = new UserIdentity(username, password);
                }
                else
                {
                    // 默认为匿名登录，OpcUaHelper 构造函数中已处理，或者可以显式设置
                    m_OpcUaClient.UserIdentity = new UserIdentity(new AnonymousIdentityToken());
                }

                // 连接到服务器
                await m_OpcUaClient.ConnectServer(serverUrl);

                // OpcUaHelper 内部处理连接状态，这里我们假设连接成功后 isConnected 为 true
                // OpcUaHelper 可能有自己的连接状态指示或事件，需要查阅其文档或示例
                // For now, we'll assume a successful ConnectServer call means connected.
                // We might need to check m_OpcUaClient.Connected or similar property if available.
                isConnected = true; // This might need adjustment based on OpcUaHelper's API
                ConnectionStatusChanged?.Invoke(true);

                // OpcUaHelper might have its own events for data changes and keep-alive.
                // The old Session_KeepAlive, Session_SessionClosing, Session_Notification handlers are removed.
                // We will need to adapt data change notifications when implementing subscription.

                return new OperateResult();
            }
            catch (Exception ex)
            {
                isConnected = false;
                ConnectionStatusChanged?.Invoke(false);
                return new OperateResult(ex.Message);
            }
        }

        // Removed Session_KeepAlive, Session_ReconnectComplete, Session_SessionClosing, Session_Notification
        // These will be handled by OpcUaHelper or re-implemented using OpcUaHelper's mechanisms.

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            try
            {
                if (m_OpcUaClient != null)
                {
                    m_OpcUaClient.Disconnect();
                }
                isConnected = false;
                ConnectionStatusChanged?.Invoke(false);
            }
            catch (Exception ex)
            {
                LogManager.LogError(ex.Message);
                // Log or handle disconnection error
                // 断开连接时发生错误，静默处理
            }
        }

        public Task<OperateResult<T>> ReadAsync<T>(string nodeId)
        {
            try
            {
                if (!isConnected)
                {
                    return Task.FromResult(new OperateResult<T>("未连接到服务器"));
                }

                // 使用 OpcUaHelper 读取节点值
                var readResult = m_OpcUaClient.ReadNode<T>(nodeId);
                if (readResult != null)
                {
                    return Task.FromResult(new OperateResult<T>(readResult));
                }
                else
                {
                    return Task.FromResult(new OperateResult<T>("读取节点值失败"));
                }
            }
            catch (Exception ex)
            {
                return Task.FromResult(new OperateResult<T>(ex.Message));
            }
        }

        public Task<OperateResult<object[]>> ReadAsync(NodeId[] nodeIds)
        {
            try
            {
                if (!isConnected)
                {
                    return Task.FromResult(new OperateResult<object[]>("未连接到服务器"));
                }

                // 使用 OpcUaHelper 批量读取节点值
                List<DataValue> values = m_OpcUaClient.ReadNodes(nodeIds);

                // Convert List<DataValue> to object[] by extracting the Value property of each DataValue
                object[] result = values.Select(value => value.Value).ToArray();

                return Task.FromResult(new OperateResult<object[]>(result));
            }
            catch (Exception ex)
            {
                return Task.FromResult(new OperateResult<object[]>(ex.Message));
            }
        }


        /// <summary>
        /// 写入单个节点值
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="nodeId">节点ID</param>
        /// <param name="value">要写入的值</param>
        /// <returns>写入结果</returns>
        public async Task<OperateResult> WriteAsync<T>(string nodeId, T value)
        {
            try
            {
                if (!isConnected)
                {
                    return new OperateResult("未连接到服务器");
                }
                // 使用 OpcUaHelper 写入节点值
                bool success = await m_OpcUaClient.WriteNodeAsync<T>(nodeId, value);
                return success ? new OperateResult() : new OperateResult("写入失败");
            }
            catch (Exception ex)
            {
                return new OperateResult(ex.Message);
            }
        }

        /// <summary>
        /// 批量写入节点值
        /// </summary>
        /// <param name="nodeIds">节点ID数组</param>
        /// <param name="values">要写入的值数组</param>
        /// <returns>写入结果</returns>
        public async Task<OperateResult> WriteAsync(string[] nodeIds, object[] values)
        {
            try
            {
                if (!isConnected)
                {
                    return new OperateResult("未连接到服务器");
                }

                if (nodeIds.Length != values.Length)
                {
                    return new OperateResult("节点ID和值的数量不匹配");
                }

                // 使用 OpcUaHelper 批量写入节点值
                // OpcUaHelper 可能没有直接的 WriteNodesAsync(string[], object[])
                // 需要确认 OpcUaHelper 是否支持批量写入，以及其API
                // 假设 OpcUaHelper 有一个 WriteNodesAsync 方法，如下所示
                // bool success = await m_OpcUaClient.WriteNodesAsync(nodeIds, values);
                // 如果没有，可能需要循环调用 WriteNodeAsync
                // 或者 OpcUaHelper.OpcUaClient.WriteNodes(NodeId[], DataValue[])

                for (int i = 0; i < nodeIds.Length; i++)
                {
                    bool success = await m_OpcUaClient.WriteNodeAsync(nodeIds[i], values[i]);
                    if (!success)
                    {
                        // 如果任何一个节点写入失败，则返回失败结果
                        return new OperateResult($"节点 {nodeIds[i]} 写入失败");
                    }
                }

                return new OperateResult(); // 所有节点写入成功
            }
            catch (Exception ex)
            {
                return new OperateResult(ex.Message);
            }
        }

        /// <summary>
        /// 订阅节点数据变化
        /// </summary>
        /// <param name="nodeId">节点ID</param>
        /// <param name="callback">回调函数</param>
        /// <returns>订阅结果</returns>
        public OperateResult Subscribe(string nodeId, Action<string, object> callback)
        {
            if (!isConnected)
            {
                return new OperateResult("未连接到服务器");
            }

            try
            {
                // 使用OpcUaHelper的AddSubscription方法订阅节点变化
                // 第一个参数是key，第二个参数是nodeId
                m_OpcUaClient.AddSubscription(nodeId, nodeId, (itemName, monitoredItem, eventArgs) =>
                {
                    try
                    {
                        // 从事件参数中获取变化的值
                        object value = null;

                        // 按照标准方式处理订阅回调
                        if (eventArgs?.NotificationValue is MonitoredItemNotification notification)
                        {
                            // 从notification.Value.WrappedValue.Value获取实际值
                            value = notification.Value.WrappedValue.Value;
                        }
                        else if (monitoredItem?.LastValue != null)
                        {
                            // 备用方案：从MonitoredItem获取最新值
                            value = monitoredItem.LastValue;
                        }
                        else
                        {
                            return;
                        }

                        // 触发数据变化事件
                        DataChanged?.Invoke(itemName, value);
                        callback?.Invoke(itemName, value);
                    }
                    catch (Exception ex)
                    {
                        // 订阅回调处理异常，静默处理
                    }
                });

                return new OperateResult();
            }
            catch (Exception ex)
            {
                return new OperateResult(ex.Message);
            }
        }

        public OperateResult Unsubscribe(string nodeId)
        {
            if (!isConnected)
            {
                return new OperateResult("未连接到服务器");
            }

            try
            {
                // Assuming the publishName used in AddSubscription was the nodeId itself.
                m_OpcUaClient.RemoveSubscription(nodeId);
                return new OperateResult();
            }
            catch (Exception ex)
            {
                return new OperateResult(ex.Message);
            }
        }

        /// <summary>
        /// 读取历史数据
        /// </summary>
        /// <param name="nodeId">节点ID</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <returns>历史数据</returns>
        public Task<OperateResult<object[]>> ReadHistoryAsync(string nodeId, DateTime startTime, DateTime endTime)
        {
            if (!isConnected)
            {
                return Task.FromResult(new OperateResult<object[]>("未连接到服务器"));
            }

            // OpcUaHelper's capabilities for history reading are not immediately clear from the README.
            // For now, we will assume it's not directly supported or requires a different approach.
            // This method can be updated if OpcUaHelper provides a way to read history.
            // ReadHistoryAsync功能待实现
            return Task.FromResult(new OperateResult<object[]>("历史数据读取功能当前不受 OpcUaHelper 支持或实现方式未知"));

        }

        /// <summary>
        /// 是否已连接
        /// </summary>
        // OpcUaHelper might have its own IsConnected property. 
        // For now, we rely on the isConnected flag updated during Connect/Disconnect calls.
        // If m_OpcUaClient.IsConnected exists, it would be more accurate to use:
        // public bool IsConnected => m_OpcUaClient != null && m_OpcUaClient.IsConnected;
        public bool IsConnected => isConnected;

        /// <summary>
        /// 浏览节点的子节点
        /// </summary>
        /// <param name="nodeId">要浏览的节点ID</param>
        /// <returns>子节点列表</returns>
        public async Task<OperateResult<Opc.Ua.ReferenceDescription[]>> BrowseAsync(string nodeId)
        {
            return await Task.Run(() =>
            {
                try
                {
                    if (m_OpcUaClient == null || !isConnected)
                    {
                        return new OperateResult<Opc.Ua.ReferenceDescription[]>("OPC UA客户端未连接");
                    }

                    // 使用OpcUaHelper的Browse方法
                    var browseResult = m_OpcUaClient.BrowseNodeReference(nodeId);
                    if (browseResult != null)
                    {
                        return new OperateResult<Opc.Ua.ReferenceDescription[]>(browseResult);
                    }
                    else
                    {
                        return new OperateResult<Opc.Ua.ReferenceDescription[]>("浏览节点失败：未获取到节点信息");
                    }
                }
                catch (Exception ex)
                {
                    return new OperateResult<Opc.Ua.ReferenceDescription[]>($"浏览节点失败: {ex.Message}");
                }
            });
        }

        /// <summary>
        /// 浏览节点的子节点（异步版本）
        /// </summary>
        /// <param name="nodeId">要浏览的节点ID</param>
        /// <returns>子节点列表</returns>
        public async Task<OperateResult<Opc.Ua.ReferenceDescription[]>> BrowseNodeAsync(string nodeId)
        {
            return await Task.Run(() => BrowseAsync(nodeId));
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            // Ensure disconnection and release of OpcUaHelper resources.
            // OpcUaHelper's Disconnect should handle closing connections.
            // If OpcUaHelper's client is IDisposable, it should be disposed here.
            // For now, we assume Disconnect is sufficient, but this might need review
            // based on OpcUaHelper's IDisposable implementation.

            // Remove all subscriptions if OpcUaHelper provides a method for it.
            // m_OpcUaClient.RemoveAllSubscription(); // If such a method exists

            Disconnect(); // This calls m_OpcUaClient.Disconnect()

            // If m_OpcUaClient itself is IDisposable, call its Dispose method.
            // (m_OpcUaClient as IDisposable)?.Dispose(); 
            // Based on OpcUaHelper source, OpcUaHelper.OpcUaClient is IDisposable.
            if (m_OpcUaClient != null)
            {
                m_OpcUaClient.Disconnect();
                m_OpcUaClient = null;
            }
        }
    }
}